/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package gtracker;

import java.io.*;
import java.net.*;
import java.util.*;
import java.security.MessageDigest;

/**
 * The RequestParser class is responsible
 * for serving a clients request.
 * @author Jithesh, Nevil, Saurav, Sajin ,Vineeth
 */
public class RequestParser implements Runnable {

	/**
	 * The constructor.
	 * @param clientSock The TCP Socket to the client
	 * which is to be served.
	 */
	public RequestParser(Socket clientSock, GalaxiaTrackerUI gtUI, Database tDB) {
		clientSocket = clientSock;
		parentTracker = gtUI;
		trackerDB = tDB;
		parseRequest = new Thread(this, "Parse_Request");
		parseRequest.start();
	}

	@Override
	public void run() {
        DataInputStream fromClient = null;
        DataOutputStream toClient = null;
		try {
			fromClient = new DataInputStream(clientSocket.getInputStream());
			toClient = new DataOutputStream(clientSocket.getOutputStream());

			byte[] request = new byte[1024];
			GroupInfo gInfo;
		    Vector<String> uList;
			String userName, inviterUName, inviteeUName;
			String sha1FileName;
			String gID;
			String replyMessage;
			boolean ValidMember;
            int gid;
			
			fromClient.read(request);

			StringTokenizer st = new StringTokenizer(new String(request), " ");

			String msgType = st.nextToken();
			switch (Integer.parseInt(msgType)) {
				case 0:
					String dbUname = st.nextToken();
					String dbPass = st.nextToken();
					if (trackerDB.isAuthenticUser(dbUname, dbPass) && !(parentTracker.getDynInfo().isLoggedIn(dbUname))) {
						File servConfigFile = new File("tracker.cfg");
						String PassPhrase = "";
						try {
							BufferedReader configReader = new BufferedReader(new FileReader(servConfigFile));
							PassPhrase = configReader.readLine();
						} catch (Exception exp) {
							PassPhrase = "";
						}

						MessageDigest sha = MessageDigest.getInstance("SHA-1");
						sha.update(PassPhrase.getBytes());

						String b64PassPhrase = new sun.misc.BASE64Encoder().encode(sha.digest());
						String replyMsg = "0" + " " + b64PassPhrase + " ";

						toClient.write(replyMsg.getBytes());
						parentTracker.getDynInfo().addUser(dbUname, clientSocket.getInetAddress().getHostAddress());

						System.out.println("Current Tracker user list has " + parentTracker.getDynInfo().getSize() + " members");

					} else {
						toClient.write("0 ".getBytes());
					}
					break;
				case 1: // Create Group
					String gReply = "1 " + parentTracker.getCurrentGID() + " ";
					toClient.write(gReply.getBytes());

					// Update DD
					String gUName = st.nextToken();
					int cGID = parentTracker.getCurrentGID();
					parentTracker.getDynInfo().getUserInfo(gUName).addGroup(cGID);
					/*java.util.Vector<Integer> gList = parentTracker.getDynInfo().getUserInfo(gUName).getGidList();
					System.out.print("User " + gUName + " is a member of groups: ");
					for(int i = 0; i < gList.size(); i++) System.out.print(gList.elementAt(i) + ", ");*/
					
					if (parentTracker.gidMap.get(cGID) != null) {
						gInfo = (GroupInfo) parentTracker.gidMap.get(cGID);
					} else {
						gInfo = new GroupInfo();
					}
					uList = gInfo.userList;

					uList.add(gUName);
					parentTracker.gidMap.put(cGID, gInfo);

					synchronized (this) {
						parentTracker.setCurrentGID(parentTracker.getCurrentGID() + 1);
					}

					break;
				case 2:
                    int subType = Integer.parseInt(st.nextToken());
                    if(subType == 0) {
                        // Inviter
                        userName = st.nextToken();
                        inviteeUName = st.nextToken();
                        gid = Integer.parseInt(st.nextToken());
                        int pushPort;

                        if(parentTracker.gidMap.get(gid).isMemberOf(userName)) {
                            pushPort = parentTracker.getPushPort();
                            replyMessage = "2 1 " + userName + " " + gid + " ";
                            Socket toInvitee = new Socket(parentTracker.dynInfo.getUserInfo(inviteeUName).getIP(), pushPort);
                            DataOutputStream invDOS = new DataOutputStream(toInvitee.getOutputStream());
                            DataInputStream invDIS = new DataInputStream(toInvitee.getInputStream());

                            invDOS.write(replyMessage.getBytes());
                            byte[] rpMsg = new byte[128];
                            invDIS.read(rpMsg);
                            replyMessage = "" + new String(rpMsg);
                            if(replyMessage.startsWith("1")) {
                                Socket toInviter = new Socket(parentTracker.dynInfo.getUserInfo(userName).getIP(), pushPort);
                                DataOutputStream invtrDOS = new DataOutputStream(toInviter.getOutputStream());
                                replyMessage = "2 4 " + inviteeUName + " " + gid + " ";
                                invtrDOS.write(replyMessage.getBytes());
                                parentTracker.gidMap.get(gid).userList.add(inviteeUName);
                                parentTracker.dynInfo.getUserInfo(inviteeUName).addGroup(gid);
                            } else {
                                Socket toInviter = new Socket(parentTracker.dynInfo.getUserInfo(userName).getIP(), pushPort);
                                DataOutputStream invtrDOS = new DataOutputStream(toInviter.getOutputStream());
                                replyMessage = "2 3 " + inviteeUName + " " + gid + " ";
                                invtrDOS.write(replyMessage.getBytes());
                            }
                        }
                    }
                    
					break;
				case 3:
                    userName = st.nextToken();
                    gid = Integer.parseInt(st.nextToken());
                    // Update DD
                    parentTracker.dynInfo.getUserInfo(userName).removeGroup(gid);
                    parentTracker.gidMap.get(gid).removeUser(userName);
					break;
				case 4:
					break;
				case 5:
					userName = st.nextToken();
					String ipAddress = "";

					boolean loggedIn = parentTracker.dynInfo.isLoggedIn(userName);

					if ( loggedIn )
						ipAddress = parentTracker.dynInfo.getUserInfo(userName).getIP();

					replyMessage = "5 ";
					if ( loggedIn)
						replyMessage += "1 " + ipAddress;
					else
						replyMessage += "0 " ;
					toClient.write(replyMessage.getBytes());

					break;
				case 6:
					break;
				case 7:
					String rmUName = st.nextToken();
					if (parentTracker.getDynInfo().isLoggedIn(rmUName)) {
						
						// Remove all group memberships.
						for ( Integer grpId : parentTracker.getDynInfo().getUserInfo(rmUName).getGidList()){
							parentTracker.gidMap.get(grpId).removeUser(rmUName);
						}
                        parentTracker.getDynInfo().removeUser(rmUName);

						System.out.println("User removed: " + rmUName);
					}
					break;
				case 8:
					userName = st.nextToken();
					sha1FileName = st.nextToken();
					gID = st.nextToken();

					gInfo = parentTracker.gidMap.get(Integer.parseInt(gID));
					if ( gInfo.isMemberOf(userName) ) {
						gInfo.addAsPeer( userName , sha1FileName);
						ValidMember = true;
					}
					else
						ValidMember = false;

					replyMessage = "8 ";
					if (ValidMember )
						replyMessage += "1 ";
					else
						replyMessage += "0 ";
					toClient.write(replyMessage.getBytes());

					break;
				case 9:
					userName = st.nextToken();
					sha1FileName = st.nextToken();
					gID = st.nextToken();
					Vector<String> peerList = new Vector<String>();

					gInfo = parentTracker.gidMap.get(Integer.parseInt(gID));
					if ( gInfo.isMemberOf(userName) ) {
						peerList = gInfo.getPeers(sha1FileName);
						
						ValidMember = true;
					}
					else
						ValidMember = false;
					replyMessage = "9 ";
					if ( ValidMember )
						replyMessage += "1 "  + peerList + " ";
					else
						replyMessage += "0 ";
					toClient.write(replyMessage.getBytes());
                    break;
                case 10: // Return user list
                    userName = st.nextToken();
                    gid = Integer.parseInt(st.nextToken());
                    Vector<String> userList = parentTracker.dynInfo.getUserList();
                    for(String s : parentTracker.gidMap.get(gid).getGroupUserList()) {
                        userList.remove(s);
                    }
                    /*if(userList.isEmpty()) {
                        replyMessage = " ";
                    } else { */
                        replyMessage = "";
                        for (String s : userList) {
                            replyMessage = replyMessage + s + " ";
                        }
                    //}
                    toClient.write(replyMessage.getBytes());
                    break;
                case 16:
                    userName = st.nextToken();
                    gid = Integer.parseInt(st.nextToken());
                    Vector<String> groupUserList = parentTracker.gidMap.get(gid).getGroupUserList();
                    groupUserList.remove(userName);
                    replyMessage = "" + groupUserList.size() + " ";
                    for(String s : groupUserList) {
                        replyMessage = replyMessage + s + " ";
                    }
                    toClient.write(replyMessage.getBytes());
                    break;
				default:
					//toClient.write( request );
					break;
			}

		} catch (Exception e) {
			System.err.println("Error Occured " + e.toString());
		} finally {
            try {
                toClient.close();
                fromClient.close();
                clientSocket.close();
            } catch (IOException iOException) {
            }
        }
	}
	protected Thread parseRequest;
	protected Socket clientSocket;
	protected Database trackerDB;
//    protected DynInfo dynInfo;
	protected GalaxiaTrackerUI parentTracker;
}

